/*
 * %W% %E%
 *
 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.corba.se.impl.ior;

import java.io.IOException ;

import org.omg.CORBA.MARSHAL ;
import org.omg.CORBA.OctetSeqHolder ;
import org.omg.CORBA_2_3.portable.InputStream ;

import com.sun.corba.se.spi.ior.ObjectId ;
import com.sun.corba.se.spi.ior.ObjectKey ;
import com.sun.corba.se.spi.ior.ObjectKeyFactory ;
import com.sun.corba.se.spi.ior.ObjectKeyTemplate ;

import com.sun.corba.se.spi.orb.ORB ;
import com.sun.corba.se.spi.logging.CORBALogDomains ;

import com.sun.corba.se.impl.orbutil.ORBConstants ;

import com.sun.corba.se.impl.ior.JIDLObjectKeyTemplate ;
import com.sun.corba.se.impl.ior.POAObjectKeyTemplate ;
import com.sun.corba.se.impl.ior.WireObjectKeyTemplate ;
import com.sun.corba.se.impl.ior.ObjectIdImpl ;
import com.sun.corba.se.impl.ior.ObjectKeyImpl ;
import com.sun.corba.se.impl.logging.IORSystemException ;

import com.sun.corba.se.impl.encoding.EncapsInputStream ;

/** Based on the magic and scid, return the appropriate 
* ObjectKeyTemplate.  Expects to be called with a valid
* magic.  If scid is not valid, null should be returned.
*/
interface Handler {
    ObjectKeyTemplate handle( int magic, int scid, 
	InputStream is, OctetSeqHolder osh ) ;
}

/** Singleton used to manufacture ObjectKey and ObjectKeyTemplate
 * instances.
 * @author Ken Cavanaugh
 */
public class ObjectKeyFactoryImpl implements ObjectKeyFactory
{
    public static final int MAGIC_BASE			= 0xAFABCAFE ;

    // Magic used in our object keys for JDK 1.2, 1.3, RMI-IIOP OP,
    // J2EE 1.0-1.2.1.
    public static final int JAVAMAGIC_OLD		= MAGIC_BASE ;

    // Magic used only in JDK 1.3.1.  No format changes in object keys.
    public static final int JAVAMAGIC_NEW		= MAGIC_BASE + 1 ;

    // New magic used in our object keys for JDK 1.4, J2EE 1.3 and later.
    // Format changes: all object keys have version string; POA key format
    // is changed.
    public static final int JAVAMAGIC_NEWER		= MAGIC_BASE + 2 ;

    public static final int MAX_MAGIC			= JAVAMAGIC_NEWER ;

    // Beginning in JDK 1.3.1_01, we introduced changes which required
    // the ability to distinguish between JDK 1.3.1 FCS and the patch
    // versions.  See OldJIDLObjectKeyTemplate.
    public static final byte JDK1_3_1_01_PATCH_LEVEL = 1;  

    private final ORB orb ;
    private IORSystemException wrapper ;

    public ObjectKeyFactoryImpl( ORB orb ) 
    {
	this.orb = orb ;
	wrapper = IORSystemException.get( orb,
	    CORBALogDomains.OA_IOR ) ;
    }
   
    // XXX The handlers still need to be made pluggable.
    //
    // I think this can be done as follows:
    // 1. Move the Handler interface into the SPI as ObjectKeyHandler.
    // 2. Add two methods to ObjectAdapterFactory:
    //	    ObjectKeyHandler getHandlerForObjectKey( ) ;
    //	    ObjectKeyHandler getHandlerForObjectKeyTemplate( ) ;
    // 3. Move the implementation of the fullKey handler and the
    //    oktempOnly handler into TOAFactory and POAFactory.
    // 4. Move the ObjectKey impl classes into the impl/oa packages.
    // 5. Create an internal interface
    //	    interface HandlerFinder {
    //		ObjectKeyHandler get( int scid ) ;
    //	    }
    //    and modify create(InputStream,Handler,OctetSeqHolder) 
    //    to take a HandlerFinder instead of a Handler.
    // 6. Modify create( byte[] ) and createTemplate( InputStream )
    //    to create an instance of HandlerFinder: something like:
    //	    new HandlerFinder() {
    //		ObjectKeyHandler get( int scid ) 
    //		{
    //		    return orb.getRequestDispatcherRegistry().
    //			getObjectAdapterFactory( scid ).getHandlerForObjectKey() ;
    //		}
    //	    and similarly for getHandlerForObjectKeyTemplate.

    /** This handler reads the full object key, both the oktemp
    * and the ID.
    */
    private Handler fullKey = new Handler() {
	public ObjectKeyTemplate handle( int magic, int scid, 
	    InputStream is, OctetSeqHolder osh ) {
		ObjectKeyTemplate oktemp = null ;

		if ((scid >= ORBConstants.FIRST_POA_SCID) && 
		    (scid <= ORBConstants.MAX_POA_SCID)) {
		    if (magic >= JAVAMAGIC_NEWER)
			oktemp = new POAObjectKeyTemplate( orb, magic, scid, is, osh ) ;
		    else
			oktemp = new OldPOAObjectKeyTemplate( orb, magic, scid, is, osh ) ;
		} else if ((scid >= 0) && (scid < ORBConstants.FIRST_POA_SCID)) {
		    if (magic >= JAVAMAGIC_NEWER)
			oktemp = new JIDLObjectKeyTemplate( orb, magic, scid, is, osh ) ;
		    else
			oktemp = new OldJIDLObjectKeyTemplate( orb, magic, scid, is, osh );
		}

		return oktemp ;
	    }
	} ;

    /** This handler reads only the oktemp.
    */
    private Handler oktempOnly = new Handler() {
	public ObjectKeyTemplate handle( int magic, int scid, 
	    InputStream is, OctetSeqHolder osh ) {
		ObjectKeyTemplate oktemp = null ;

		if ((scid >= ORBConstants.FIRST_POA_SCID) && 
		    (scid <= ORBConstants.MAX_POA_SCID)) {
		    if (magic >= JAVAMAGIC_NEWER)
			oktemp = new POAObjectKeyTemplate( orb, magic, scid, is ) ;
		    else
			oktemp = new OldPOAObjectKeyTemplate( orb, magic, scid, is ) ;
		} else if ((scid >= 0) && (scid < ORBConstants.FIRST_POA_SCID)) {
		    if (magic >= JAVAMAGIC_NEWER)
			oktemp = new JIDLObjectKeyTemplate( orb, magic, scid, is ) ;
		    else
			oktemp = new OldJIDLObjectKeyTemplate( orb, magic, scid, is ) ;
		}

		return oktemp ;
	    }
	} ;

    /** Returns true iff magic is in the range of valid magic numbers
    * for our ORB.
    */
    private boolean validMagic( int magic )
    {
	return (magic >= MAGIC_BASE) && (magic <= MAX_MAGIC) ;
    }

    /** Creates an ObjectKeyTemplate from the InputStream.  Most of the
    * decoding is done inside the handler.  
    */
    private ObjectKeyTemplate create( InputStream is, Handler handler, 
	OctetSeqHolder osh ) 
    {
	ObjectKeyTemplate oktemp = null ;
	
	try {
	    is.mark(0) ;
	    int magic = is.read_long() ;
		    
	    if (validMagic( magic )) {
		int scid = is.read_long() ;
		oktemp = handler.handle( magic, scid, is, osh ) ;
	    }
	} catch (MARSHAL mexc) {
	    // XXX log this error
	    // ignore this: error handled below because oktemp == null
	} 

	if (oktemp == null)
	    // If we did not successfully construct a oktemp, reset the 
	    // stream so that WireObjectKeyTemplate can correctly construct the
	    // object key.
	    try {
		is.reset() ;
	    } catch (IOException exc) {
		// XXX log this error
		// ignore this
	    }

	return oktemp ;
    }

    public ObjectKey create( byte[] key ) 
    {
	OctetSeqHolder osh = new OctetSeqHolder() ;
	EncapsInputStream is = new EncapsInputStream( orb, key, key.length ) ;

	ObjectKeyTemplate oktemp = create( is, fullKey, osh ) ;
	if (oktemp == null)
	    oktemp = new WireObjectKeyTemplate( is, osh ) ;

	ObjectId oid = new ObjectIdImpl( osh.value ) ;
	return new ObjectKeyImpl( oktemp, oid ) ;
    }

    public ObjectKeyTemplate createTemplate( InputStream is ) 
    {
	ObjectKeyTemplate oktemp = create( is, oktempOnly, null ) ;
	if (oktemp == null)
	    oktemp = new WireObjectKeyTemplate( orb ) ;

	return oktemp ;
    }
}
